package tp.pr5.items;

import java.util.ArrayList;
import java.util.List;

import tp.pr5.Constants;
import tp.pr5.Observable;

/**
 * A container of items. It can be employed by any class that stores items. A container cannot store two items with the same identifier
 * It provides methods to add new items, access them and remove them from the container.
 *
 */
public class ItemContainer extends Observable<InventoryObserver>
{

	private Item[] item;
	
	/**
	 * Default constructor. It creates an array of items
	 */
	public ItemContainer()
	{
		this.item = new Item[100];
	}
	
	/**
	 * Returns the number of items contained
	 * 
	 * @return the number of items in the container
	 */
	public int numberOfItems() 
	{
		int itemCounter = 0;
		
		for(int i = 0; i < item.length; i++)
		{
			if(item[i] != null) itemCounter++;
		}
		return itemCounter;
	}
	
	/**
	 * Add an item to the container. The operation can fail, returning false
	 * 
	 * @param item The name of the item to be added.
	 * 
	 * @return true if and only if the item is added, i.e., an item with the same identifier does not exists in the container
	 */
	public boolean addItem(Item item)
	{
		int posItem = positionItem(item.getId());
		
		if(posItem != -1)
		{
			return false;
		}
		else
		{
			this.item[this.numberOfItems()] = item;
			this.ordenar();
			this.emitInventoryChange();
			
			return true;
		}				
	}
	
	/**
	 * Returns the item from the container according to the item name
	 * 
	 * @param id Item name
	 * 
	 * @return Item with that name or null if the container does not store an item with that name.
	 */
	public Item getItem(String id)
	{
		int posItem = positionItem(id);
		
		if(posItem != -1)
		{
			return this.item[posItem];
		}
		else
		{
			return null;
		}
	}
	
	/**
	 * Returns and deletes an item from the inventory. This operation can fail, returning null
	 * 
	 * @param id Name of the item
	 * 
	 * @return An item if and only if the item identified by id exists in the inventory. Otherwise it returns null
	 */
	public Item pickItem(String id)
	{
		int pos = positionItem(id);
		Item it = null;

		if(pos >= 0)
		{
			it = this.item[pos];
			this.item[pos] = null;
			this.movePosition(pos);
			this.emitInventoryChange();
		}
		
		return it;
	}
	
	/**
	 * Move all items one position to the left
	 * 
	 * @param position
	 */
	public void movePosition(int position)
	{	
		for (int i = position + 1; i < numberOfItems() + 1; i++) 
		{
			item[i-1] = item[i];			
		}

	}
	
	/**
	 * Generates a String with information about the items contained in the container. Note that the items must appear sorted but the item name.
	 */
	public String toString()
	{
		/*String items = "   ";
		
		for (int i = 0; i < this.item.length; i++)
		{
			if(item[i] != null)
			{
				items += item[i].getId() + Constants.LINE_SEPARATOR + "   ";
			}
		}
		return items;*/
		String items = "   ";
		Item lastItem = null;
		
		for (int i = 0; i < this.item.length; i++)
		{
			if(item[i] != null)
			{
				if(item[i+1] != null)
				{
					items += item[i].getId() + Constants.LINE_SEPARATOR + "   ";
				}
				else
				{
					lastItem = item[i];
				}
			}
		}
		return items + lastItem.getId() + Constants.LINE_SEPARATOR;

	}
	
	/**
	 * Get the array position where the item is stored
	 * 
	 * @param id identifier item name
	 * 
	 * @return int position in the array
	 */
	public int positionItem(String id)
	{
		int aux = -1;

		for (int i = 0; i < this.item.length; i++) 
		{
			if(this.item[i] != null)
			{
				if(this.item[i].getId().equalsIgnoreCase(id))
				{
					aux = i;
				}
			}
		}
		return aux;
	}
	
	/**
	 * Get the array position where the item has to be inserted
	 * 
	 * @param Array Item array of items
	 * 
	 * @return int position 
	 */
	public int positionInsert(Item[] item)
	{
		
		for(int i = 0; i < item.length; i++)
		{
			if(item[i] == null)
			{
				return i;
			}
		}
		return -1;
		
	}
	
	/**
	 * Sort the array in alphabetical
	 * 
	 */
	public void ordenar()
	{

		Item aux;
		
		for (int i = 0; i < this.numberOfItems() / 2 + 1 ; i++)
		{
			for (int j = 0; j < this.numberOfItems() - 1 ; j++) 
			{
				if(this.item[j] != null)
				{
					if(this.item[j].getId().compareToIgnoreCase(this.item[j+1].getId()) > 0)
					{
						aux = this.item[j];
						this.item[j] = this.item[j+1];
						this.item[j+1] = aux;
					}
				}
			}
		}
	}
	
	/**
	 * Checks if the Item with this id exists in the container.
	 * 
	 * @param id Name of the item.
	 * @return true if the container as an item with that name.
	 */
	public boolean containsItem(String id)
	{
		if(numberOfItems() > 0)
		{
			for (int i = 0; i < this.numberOfItems(); i++) 
			{
				if(item[i].getId().equalsIgnoreCase(id))
				{
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * PRECOND: The item exists
	 * 
	 * @param id - ID of the item to be scanned
	 */
	public void requestScanItem(String id)
	{
		Item item = getItem(id); 
		
		if(item != null)
		{
			this.emitItemScanned(item.toString());
		}

	}
	
	/**
	 * Request to scan the whole inventory
	 * 
	 */
	public void requestScanCollection()
	{
		this.emitInventoryScanned(this.toString());
	}
	
	/**
	 * Notifies that the user wants to scan an item allocated in the inventory 
	 * 
	 * @param id - ID of the item to scan
	 */
	public void emitItemScanned(String id)
	{
		for (InventoryObserver nObserver : this.lista)
		{
			nObserver.itemScanned(id);
		}
	}
	
	/**
	 * Notifies that the container has changed
	 * 
	 */
	public void emitInventoryChange()
	{
		for (InventoryObserver nObserver : this.lista)
		{
			nObserver.inventoryChange(this.arrayToList());
		}
	}
	
	/**
	 * Notifies that the user requests a SCAN instruction over the inventory.
	 * 
	 * @param inventoryDescription
	 */
	public void emitInventoryScanned(String inventoryDescription) 
	{
		for (InventoryObserver nObserver : this.lista)
		{
			nObserver.inventoryScanned(inventoryDescription);
		}
	}
	
	/**
	 * Notifies that an item is empty and it will be removed from the container.
	 * 
	 * @param itemName - the item that has to be removed
	 */
	public void emitItemEmpty(String itemName) 
	{
		for (InventoryObserver nObserver : this.lista)
		{
			nObserver.itemEmpty(itemName);
		}
	}
	
	/**
	 * Method called by the OperateInstruction when an item stored in the collection is successfully used. 
	 * The collection then checks if the item could be used again in the future. 
	 * If it is not possible because the item is "empty", then it is removed from the collection (and the method notifies all the observers).
	 * 
	 * @param item - to be used
	 */
	public void useItem(Item item)
	{
		String it = item.getId();

		if(!item.canBeUsed())
		{			
			this.pickItem(it);
			this.emitItemEmpty(it);
		}
		this.emitInventoryChange();
	}
	
	/**
	 * Converts the current Array to List
	 * 
	 * @return a List of the items placed in the item container
	 */
	public List<Item> arrayToList()
	{
		List<Item> lista = new ArrayList<Item>();
		
		for (int i = 0; i < this.numberOfItems(); i++) 
		{
			lista.add(this.item[i]);
		}
		
		return lista;
	}

	
}
